using System;
using TestCases.CompareAssert.Core.Comparers.Basic;
using TestCases.CompareAssert.Core.Comparers.Collections;
using TestCases.CompareAssert.Core.Comparers.Composite;
using TestCases.CompareAssert.Core.Comparers.Generics;
using TestCases.CompareAssert.Core.Comparers.Interfaces;
using TestCases.CompareAssert.Core.Comparers.Members;
using TestCases.CompareAssert.Core.Comparers.Stateless;
using TestCases.CompareAssert.Core.Interfaces;

namespace TestCases.CompareAssert.Core.Comparers
{
    public class DefaultComparer : IObjectComparer
    {
        public const string IdentifiableName = "DefaultComparer";
        private static readonly DefaultComparer _instance = new DefaultComparer();
// Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
// ReSharper disable EmptyConstructor

        private readonly IObjectComparer _comparer;

        static DefaultComparer()
// ReSharper restore EmptyConstructor
        {
        }
        
        public static bool IsSimpleType(Type type)
        {
            return type.IsPrimitive 
                || type.IsPointer 
                || type.IsEnum 
                || type == typeof(string);
        }
        
        private DefaultComparer()
        {
            _comparer = CompositeComparer.Single(
                NullComparer.Instance,
                CompositeComparer.Any(
                    ReferenceComparer.Instance,                    
                    CompositeComparer.All(
                        new SetupComparer(SetupEqualsComparer, EqualsComparer.Instance), 
                        TypeComparer.Instance,                        
                        /* All comparers which can return NotApplicable must be in Single compositor */
                        CompositeComparer.Single(
                            EquatableComparer.Instance,
                            ComparableComparer.Instance,
                            new DictionaryComparer(this),
                            new ArraysComparer(this),
                            new CollectionsComparer(this),
                            new EnumerableComparer(this),
                            new CircularReferencesComparer(
                                CompositeComparer.Single(
                                    new PropertiesComparer(this),
                                    new FieldsComparer(this)
                                    )
                                )
                            )
                        )
                    )
                );
                
        }

        private static SetupStatus SetupEqualsComparer(IObjectComparer comparer, ICompareContext context)
        {
            return IsSimpleType(context.LType) ? SetupStatus.Default : SetupStatus.Escape;
        }

        public static DefaultComparer Instance
        {
            get { return _instance; }
        }


        public string Name
        {
            get { return IdentifiableName; }
        }

        public IResultBuilder Compare(ICompareContext context)
        {
            return _comparer.Compare(context);
        }

    }
}